package com.jrfid.smartcabinet.demo.ui.add_face

import android.annotation.SuppressLint
import android.content.DialogInterface
import android.graphics.ImageFormat
import android.graphics.Point
import android.hardware.Camera
import android.media.Image
import android.media.Image.Plane
import android.os.*
import androidx.lifecycle.ViewModelProvider
import android.util.Rational
import android.util.Size
import android.view.*
import androidx.fragment.app.Fragment
import androidx.appcompat.app.AlertDialog
import androidx.camera.core.*
import androidx.camera.core.ImageAnalysis.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import com.arcsoft.face.enums.DetectFaceOrientPriority
import com.arcsoft.face.enums.DetectMode
import com.arcsoft.face.enums.DetectModel
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ResourceUtils
import com.blankj.utilcode.util.ToastUtils
import com.jrfid.smartcabinet.demo.R
import com.jrfid.smartcabinet.demo.arcface.FaceServer
import com.jrfid.smartcabinet.demo.base.BaseFragment
import com.jrfid.smartcabinet.demo.databinding.AddFaceFragmentBinding
import com.jrfid.smartcabinet.demo.utils.FaceUtils
import java.nio.ByteBuffer
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import kotlin.properties.Delegates
import androidx.camera.core.ImageProxy
import androidx.camera.core.impl.ImageAnalysisConfig
import com.arcsoft.face.*
import com.blankj.utilcode.util.ThreadUtils
import com.jrfid.smartcabinet.demo.arcface.DrawHelper
import com.jrfid.smartcabinet.demo.arcface.TrackUtil
import com.jrfid.smartcabinet.demo.arcface.camera.CameraHelper
import com.jrfid.smartcabinet.demo.arcface.camera.CameraListener
import com.jrfid.smartcabinet.demo.arcface.face.FaceHelper
import com.jrfid.smartcabinet.demo.arcface.face.FaceListener
import com.jrfid.smartcabinet.demo.arcface.face.LivenessType
import com.jrfid.smartcabinet.demo.model.DrawFaceInfoModel
import com.jrfid.smartcabinet.demo.utils.ImageUtil
import com.jrfid.smartcabinet.demo.utils.ImageUtils
import com.jrfid.smartcabinet.demo.view.FaceRectView
import java.lang.Exception
import java.util.*
import java.util.concurrent.Executor
import java.util.concurrent.locks.ReentrantLock


class AddFaceFragment : BaseFragment<AddFaceFragmentBinding, AddFaceViewModel>(AddFaceViewModel::class.java) {

    companion object {
        fun newInstance() = AddFaceFragment()
    }

    override fun getLayoutId(): Int = R.layout.add_face_fragment

    private var isNeedActive = false
    private lateinit var previewView: SurfaceView
    private lateinit var faceRectView: FaceRectView

    /**
     * 当前识别到的人脸
     */
    private var currentFaceInfoModel: DrawFaceInfoModel? = null
    private var threadPoolExecutor = Executors.newFixedThreadPool(5)
    private val tagGetFaceLiveness = 100
    private val tagGetFaceFeature = 101
    private val tagFaceRegistered = 102

    private val handler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when (msg.what) {
                tagGetFaceFeature -> {
                    val faceFeature = msg.obj as FaceFeature
                    val faceId = msg.arg1
                    if (faceId == currentFaceInfoModel?.faceId) {
                        currentFaceInfoModel?.let {
                            it.faceFeature = faceFeature
                            faceRectView.setDrawFaceInfo(currentFaceInfoModel)
                        }
                    }
                }
                tagGetFaceLiveness -> {
                    val livenessInfo = msg.obj as Int
                    val faceId = msg.arg1
                    if (faceId == currentFaceInfoModel?.faceId) {
                        currentFaceInfoModel?.let {
                            it.liveness = livenessInfo
                            faceRectView.setDrawFaceInfo(currentFaceInfoModel)
                        }
                    }
                }
                tagFaceRegistered -> {
                    val result = msg.obj as Boolean
                    val faceId = msg.arg1
                    if (faceId == currentFaceInfoModel?.faceId) {
                        currentFaceInfoModel?.let {
                            it.isRegistered = result
                            faceRectView.setDrawFaceInfo(currentFaceInfoModel)
                        }
                    }
                }
            }
        }
    }

    override fun setData() {
        previewView = getDataBinding()!!.previewView
        faceRectView = getDataBinding()!!.faceView
        val activeFileInfo = ActiveFileInfo()
        val code = FaceEngine.getActiveFileInfo(requireContext(), activeFileInfo)
        if (code == ErrorInfo.MOK) {
            //正常获取人脸识别激活信息，初始化相机
            isNeedActive = false
            initFace()
        } else if (code == ErrorInfo.MERR_ASF_NOT_ACTIVATED || code == ErrorInfo.MERR_ASF_ACTIVE_FILE_NO_EXIST) {
            //激活人脸识别
            isNeedActive = true
        } else {
            isNeedActive = false
            ToastUtils.showLong(getStringFormat(R.string.text_face_init_error, code))
        }
    }

    /**
     * VIDEO模式人脸检测引擎，用于预览帧人脸追踪
     */
    private var ftEngine: FaceEngine? = null

    /**
     * 用于特征提取的引擎
     */
    private var frEngine: FaceEngine? = null

    /**
     * IMAGE模式活体检测引擎，用于预览帧人脸活体检测
     */
    private var flEngine: FaceEngine? = null

    private fun initFace() {
        ftEngine = FaceEngine()
        var code = ftEngine?.init(requireContext(), DetectMode.ASF_DETECT_MODE_VIDEO, DetectFaceOrientPriority.ASF_OP_ALL_OUT, 16, 10, FaceEngine.ASF_FACE_DETECT)
        if (code != ErrorInfo.MOK) {
            ftEngine = null
            LogUtils.dTag(TAG, "人脸识别引擎初始化失败")
        }
        frEngine = FaceEngine()
        code = frEngine?.init(requireContext(), DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_ALL_OUT, 16, 10, FaceEngine.ASF_FACE_RECOGNITION)
        if (code != ErrorInfo.MOK) {
            LogUtils.dTag(TAG, "frEngine 初始化错误，${code}")
        }
        flEngine = FaceEngine()
        code = flEngine?.init(requireContext(), DetectMode.ASF_DETECT_MODE_IMAGE, DetectFaceOrientPriority.ASF_OP_ALL_OUT, 16, 10, FaceEngine.ASF_LIVENESS)
        if (code != ErrorInfo.MOK) {
            LogUtils.dTag(TAG, "flEngine 初始化错误，${code}")
        }
    }

    private fun destroyEngine() {
        ftEngine?.let {
            synchronized(it) {
                val code = it.unInit()
                LogUtils.dTag(TAG, "ftEngine 回收，${code}")
            }
        }
        frEngine?.let {
            synchronized(it) {
                val code = it.unInit()
                LogUtils.dTag(TAG, "frEngine 回收，${code}")
            }
        }
        flEngine?.let {
            synchronized(it) {
                val code = it.unInit()
                LogUtils.dTag(TAG, "flEngine 回收，${code}")
            }
        }
    }

    private var cameraHelper: CameraHelper? = null
    private var drawHelper: DrawHelper? = null
    private var faceHelper: FaceHelper? = null
    private var previewSize: Camera.Size? = null

    private fun initCamera() {
        val cameraListener = object : CameraListener {
            override fun onCameraOpened(camera: Camera, cameraId: Int, displayOrientation: Int, isMirror: Boolean) {
                previewSize = camera.parameters.previewSize
                previewView.post {
                    drawHelper = DrawHelper(
                        previewSize!!.width,
                        previewSize!!.height,
                        previewView.width,
                        previewView.height,
                        displayOrientation,
                        cameraId,
                        isMirror,
                        false,
                        false
                    )
                }
            }

            override fun onPreview(nv21: ByteArray?, camera: Camera) {
                if (drawHelper == null) {
                    //等待初始化
                    return
                }
                if (nv21 == null) {
                    return
                }
                val data = nv21
                faceRectView.clearFaceInfo()
                val faceInfoList = arrayListOf<FaceInfo>()
                val code = ftEngine?.detectFaces(data, previewSize!!.width, previewSize!!.height, FaceEngine.CP_PAF_NV21, faceInfoList)
                if (code == ErrorInfo.MOK) {
                    if (faceInfoList.isEmpty()) {
                        LogUtils.dTag(TAG, "未识别到人脸")
                    } else {
                        //保留其中一个最大的人脸
                        TrackUtil.keepMaxFace(faceInfoList)
                        //绘制人脸框
                        val faceInfo = faceInfoList[0]
                        if (currentFaceInfoModel?.faceId == faceInfo.faceId) {
                            //没有换人，只重新绘制位置
                            currentFaceInfoModel?.let {
                                it.faceRect = drawHelper?.adjustRect(faceInfo.rect)
                                it.faceInfo = faceInfo
                                if (it.faceFeature == null) {
                                    threadPoolExecutor.execute(FaceRecognizeRunnable(data, faceInfo, previewSize!!.width, previewSize!!.height))
                                }
                                if (it.liveness != LivenessInfo.ALIVE) {
                                    threadPoolExecutor.execute(FaceLivenessDetectRunnable(data, faceInfo, previewSize!!.width, previewSize!!.height))
                                }
                            }
                        } else {
                            //换人过后，重新绘制
                            currentFaceInfoModel = DrawFaceInfoModel().apply {
                                this.faceInfo = faceInfoList[0]
                                this.faceRect = drawHelper?.adjustRect(faceInfo.rect)
                                this.faceId = faceInfoList[0].faceId
                            }
                            //先检查活体信息与特征值
                            threadPoolExecutor.execute(FaceRecognizeRunnable(data, faceInfo, previewSize!!.width, previewSize!!.height))
                            threadPoolExecutor.execute(FaceLivenessDetectRunnable(data, faceInfo, previewSize!!.width, previewSize!!.height))
                        }
                        faceRectView.setDrawFaceInfo(currentFaceInfoModel)
                    }
                } else {
                    LogUtils.dTag(TAG, "人脸识别错误：${code}")
                }
            }

            override fun onCameraClosed() {

            }

            override fun onCameraError(e: Exception?) {

            }

            override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
                drawHelper?.cameraDisplayOrientation = displayOrientation
            }
        }
        cameraHelper = CameraHelper.Builder()
            .previewViewSize(Point(previewView.measuredWidth, previewView.measuredHeight))
            //.rotation(requireActivity().windowManager.defaultDisplay.rotation)
            .rotation(Surface.ROTATION_0)
            .specificCameraId(Camera.CameraInfo.CAMERA_FACING_FRONT)
            .isMirror(false)
            .previewOn(previewView)
            .cameraListener(cameraListener)
            .build()
        cameraHelper?.init()
        cameraHelper?.start()
    }

    private fun destroyCamera() {
        cameraHelper?.release()
        cameraHelper = null
    }

    override fun onResume() {
        super.onResume()
        if (threadPoolExecutor.isShutdown) {
            threadPoolExecutor = Executors.newFixedThreadPool(2)
        }
        if (isNeedActive) {
            isNeedActive = false
            AlertDialog.Builder(requireContext())
                .setTitle(R.string.text_dialog_active_title)
                .setMessage(R.string.text_dialog_active_content)
                .setCancelable(false)
                .setNeutralButton(R.string.btn_cancel) { dialog, which -> }
                .setPositiveButton(R.string.btn_active) { dialog, which ->
                    val code = FaceEngine.activeOnline(requireContext(), FaceUtils.APP_ID, FaceUtils.SDK_KEY)
                    if (code == ErrorInfo.MOK) {
                        //激活成功，初始化相机
                        ToastUtils.showLong(getString(R.string.text_face_active_successfully))
                        initFace()
                        initCamera()
                    } else {
                        ToastUtils.showLong(getStringFormat(R.string.text_face_active_failed, code))
                    }
                }
                .show()
        } else {
            initCamera()
        }
    }

    inner class FaceRecognizeRunnable(private var data: ByteArray, private var faceInfo: FaceInfo, private var width: Int, private var height: Int) : Runnable {

        override fun run() {
            frEngine?.let {
                synchronized(it) {
                    if (currentFaceInfoModel?.faceId == faceInfo.faceId && currentFaceInfoModel?.faceFeature == null) {
                        val faceFeature = FaceFeature()
                        val code = it.extractFaceFeature(data, width, height, FaceEngine.CP_PAF_NV21, faceInfo, faceFeature)
                        if (code == ErrorInfo.MOK) {
                            if (currentFaceInfoModel?.faceId == faceInfo.faceId) {
                                var message = handler.obtainMessage(tagGetFaceFeature)
                                message.obj = faceFeature
                                message.arg1 = faceInfo.faceId
                                handler.sendMessage(message)
                                if (currentFaceInfoModel?.liveness == LivenessInfo.ALIVE) {
                                    //活体检测是真人，开始搜索时候已经注册
                                    val model = FaceServer.getInstance().getTopOfFaceLib(faceFeature)
                                    if (model != null && model.similar >= 0.8f) {
                                        //判断为已注册
                                        LogUtils.dTag(TAG, "人脸已注册")
                                        message = handler.obtainMessage(tagFaceRegistered)
                                        message.obj = true
                                        message.arg1 = faceInfo.faceId
                                        handler.sendMessage(message)
                                    } else {
                                        //未注册，开始注册
                                        val result = FaceServer.getInstance().registerNv21(requireContext(), data, width, height, faceInfo, null)
                                        if (result) {
                                            LogUtils.dTag(TAG, "人脸注册成功")
                                            message = handler.obtainMessage(tagFaceRegistered)
                                            message.obj = true
                                            message.arg1 = faceInfo.faceId
                                            handler.sendMessage(message)
                                        } else {
                                            LogUtils.dTag(TAG, "人脸注册失败")
                                        }
                                    }
                                }
                            }
                        } else {
                            LogUtils.dTag(TAG, "人脸特征检测失败：${code}")
                        }
                    }
                }
            }
        }
    }

    inner class FaceLivenessDetectRunnable(private var data: ByteArray, private var faceInfo: FaceInfo, private var width: Int, private var height: Int) : Runnable {

        override fun run() {
            flEngine?.let {
                synchronized(it) {
                    if (currentFaceInfoModel?.faceId == faceInfo.faceId && currentFaceInfoModel?.liveness != LivenessInfo.ALIVE) {
                        var code = it.process(data, width, height, FaceEngine.CP_PAF_NV21, arrayListOf(faceInfo), FaceEngine.ASF_LIVENESS)
                        var result = LivenessInfo.UNKNOWN
                        if (code == ErrorInfo.MOK) {
                            val infoList = arrayListOf<LivenessInfo>()
                            code = it.getLiveness(infoList)
                            if (code == ErrorInfo.MOK && infoList.isNotEmpty() && currentFaceInfoModel?.faceId == faceInfo.faceId) {
                                if (infoList[0].liveness == LivenessInfo.ALIVE && currentFaceInfoModel?.faceFeature != null) {
                                    //活体检测是真人，开始搜索时候已经注册
                                    val model = FaceServer.getInstance().getTopOfFaceLib(currentFaceInfoModel!!.faceFeature)
                                    if (model != null && model.similar >= 0.8f) {
                                        //判断为已注册
                                        LogUtils.dTag(TAG, "人脸已注册")
                                        val message = handler.obtainMessage(tagFaceRegistered)
                                        message.obj = true
                                        message.arg1 = faceInfo.faceId
                                        handler.sendMessage(message)
                                    } else {
                                        //未注册，开始注册
                                        val r = FaceServer.getInstance().registerNv21(requireContext(), data, width, height, faceInfo, null)
                                        if (r) {
                                            LogUtils.dTag(TAG, "人脸注册成功")
                                            val message = handler.obtainMessage(tagFaceRegistered)
                                            message.obj = true
                                            message.arg1 = faceInfo.faceId
                                            handler.sendMessage(message)
                                        } else {
                                            LogUtils.dTag(TAG, "人脸注册失败")
                                        }
                                    }
                                }
                                result = infoList[0].liveness
                            } else {
                                LogUtils.dTag(TAG, "人脸活体信息获取失败：${code}")
                                result = LivenessInfo.UNKNOWN
                            }
                        } else {
                            LogUtils.dTag(TAG, "人脸活体检测失败：${code}")
                            result = LivenessInfo.UNKNOWN
                        }
                        val message = handler.obtainMessage(tagGetFaceLiveness)
                        message.obj = result
                        message.arg1 = faceInfo.faceId
                        handler.sendMessage(message)
                    }
                }
            }
        }

    }

    override fun onPause() {
        super.onPause()
        destroyCamera()
        faceRectView.clearFaceInfo()
        threadPoolExecutor.shutdownNow()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        destroyEngine()
        handler.removeMessages(tagFaceRegistered)
        handler.removeMessages(tagGetFaceFeature)
        handler.removeMessages(tagGetFaceLiveness)
    }


}

